#!/usr/bin/env python
#coding=utf-8
import socket
import os
import errno
import math
import time
from tornado.ioloop import IOLoop
from tornado.platform.auto import set_close_exec

class UdpServer(object):
    def __init__(self, port, callbackRecv, io_loop=None):
        self.io_loop = io_loop
        self._sockets = {}  # fd -> socket object
        self._pending_sockets = []
        self._started = False
        self._port = port
        self._callbackRecv = callbackRecv
        self.bind()

    def add_sockets(self, sockets):
        if self.io_loop is None:
            self.io_loop = IOLoop.instance()

        for sock in sockets:
            self._sockets[sock.fileno()] = sock
            '''
            self.add_accept_handler(sock, self._on_recive,
                               io_loop=self.io_loop)
            '''
            self.add_accept_handler(sock, self._on_recv,
                               io_loop=self.io_loop)
            
    def bind(self, address=None, family=socket.AF_UNSPEC, backlog=25):
        sockets = self.bind_sockets(self._port, address=address, family=family,
                               backlog=backlog)
        if self._started:
            self.add_sockets(sockets)
        else:
            self._pending_sockets.extend(sockets)

    def start(self, num_processes=1):
        assert not self._started
        self._started = True
        #if num_processes != 1:
        #    process.fork_processes(num_processes)
        sockets = self._pending_sockets
        self._pending_sockets = []
        self.add_sockets(sockets)
        
    def ioloop(self):
        IOLoop.instance().start()
        
    def stop(self):
        for fd, sock in self._sockets.iteritems():
            self.io_loop.remove_handler(fd)
            sock.close()        

    def bind_sockets(self, port, address=None, family=socket.AF_UNSPEC, backlog=25):
        sockets = []
        if address == "":
            address = None
        flags = socket.AI_PASSIVE
        if hasattr(socket, "AI_ADDRCONFIG"):
            flags |= socket.AI_ADDRCONFIG
        for res in set(socket.getaddrinfo(address, port, family, socket.SOCK_DGRAM,
                                      0, flags)):
            af, socktype, proto, canonname, sockaddr = res
            sock = socket.socket(af, socktype, proto)
            set_close_exec(sock.fileno())
            if os.name != 'nt':
                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            if af == socket.AF_INET6:
                if hasattr(socket, "IPPROTO_IPV6"):
                    sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
            sock.setblocking(0)
            sock.bind(sockaddr)
            sockets.append(sock)
        return sockets
        
    def _on_recv(self, data, address, sock):
        rtval = self._callbackRecv(data)  
        if len(rtval)>4096:
            n = math.floor(len(rtval) / 4096)
            m = len(rtval) % 4096
            for i in range(0,n):
                sock.sendto(bytes(rtval[i*4096:i*4096+4096] , encoding='utf-8'), address)
                time.sleep(0.01)
            sock.sendto(bytes(rtval[(i+1)*4096:(i+1)*4096+m], encoding='utf-8'), address)
            time.sleep(0.01)
        else:
            sock.sendto(bytes(rtval, encoding='utf-8'), address)
        '''
        sck.close() 
        #sock.sendto(bytes(rtval, encoding='utf-8'), address)
        '''
        
    def add_accept_handler(self, sock, callback, io_loop=None):
        if io_loop is None:
            io_loop = IOLoop.instance()

        def accept_handler(fd, events):
            while True:
                try:
                    data, address = sock.recvfrom(4096)
                except socket.error as e:
                    if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
                        return
                    raise
                callback(data, address, sock)
        io_loop.add_handler(sock.fileno(), accept_handler, IOLoop.READ)


    def bind_unix_socket(self, file,  mode=600,  backlog=128):
        if hasattr(socket, 'AF_UNIX'):
            sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
            set_close_exec(sock.fileno())
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.setblocking(0)
            try:
                st = os.stat(file)
            except OSError as err:
                if err.errno != errno.ENOENT:
                    raise
            else:
                #if stat.S_ISSOCK(st.st_mode):
                #    os.remove(file)
                #else:
                raise ValueError("File %s exists and is not a socket", file)
            sock.bind(file)
            os.chmod(file, mode)
            sock.listen(backlog)
            return sock
        return None
